package jane.mvc.engine;

import jane.mvc.annotations.Delete;
import jane.mvc.annotations.Get;
import jane.mvc.annotations.Head;
import jane.mvc.annotations.Post;
import jane.mvc.annotations.Put;
import jane.mvc.utils.MyLog;
import jane.mvc.utils.Utils;

import java.io.File;
import java.io.IOException;
import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;

import org.apache.bcel.Repository;
import org.apache.bcel.classfile.JavaClass;

public class SanityChecker
{

	private File fileToCheck = null;
	private ArrayList<String> allItemsInApplication = null;
	private ArrayList<String> janeViewsFileNamesInApplication;
	private ArrayList<String> controllersClassesInApplication;
	private ArrayList<String> modelClassesInApplication;
	private ArrayList<String> configClassesInApplication;

	private Hashtable<String, JaneMVCMethod> methodsByWebname;

	public SanityChecker(File file)
	{
		fileToCheck = file;
		allItemsInApplication = new ArrayList<String>();
		controllersClassesInApplication = new ArrayList<String>();
		modelClassesInApplication = new ArrayList<String>();
		janeViewsFileNamesInApplication = new ArrayList<String>();
		configClassesInApplication = new ArrayList<String>();

		methodsByWebname = new Hashtable<String, JaneMVCMethod>();
	}

	public AppDescriptor getAppDescriptor()
	{

		AppDescriptor app = new AppDescriptor(this.fileToCheck,
				this.controllersClassesInApplication,
				this.modelClassesInApplication,
				this.janeViewsFileNamesInApplication,
				this.configClassesInApplication, this.methodsByWebname);

		return app;

	}

	public boolean check()
	{
		JarFile jar = null;
		boolean valid = false;

		try
		{
			jar = new JarFile(this.fileToCheck, false, JarFile.OPEN_READ);

		} catch (IOException e)
		{
			// TODO Auto-generated catch block

			e.printStackTrace();
			return valid;
		}

		if (jar != null)
		{
			Enumeration<JarEntry> entries = jar.entries();
			boolean hasConfigDir = false;
			boolean hasControllersDir = false;
			boolean hasViewsDir = false;
			boolean hasModelsDir = false;

			Utils utils = new Utils();

			for (; entries.hasMoreElements();)
			{
				JarEntry element = entries.nextElement();

				if (element.getName().equals("mvc/config/"))
				{
					hasConfigDir = true;
				} else if (element.getName().startsWith("mvc/config/")
						&& element.getName().endsWith(".class"))
				{
					configClassesInApplication.add(utils
							.jarEntryToJavaName(element.getName()));
				}

				if (element.getName().equals("mvc/controllers/"))
				{
					hasControllersDir = true;
				} else if (element.getName().startsWith("mvc/controllers/")
						&& element.getName().endsWith(".class"))
				{
					String fullclassname = utils.jarEntryToJavaName(element
							.getName());

					if (fullclassname.endsWith("Controller"))
					{
						controllersClassesInApplication.add(fullclassname);

						// TODO: only get annotated methods

						Class controllerclass = utils.getClassFromJarByName(
								fullclassname, this.fileToCheck);

						java.lang.reflect.Method[] reflectionmethods = controllerclass
								.getMethods();

						JavaClass controllerClassAnalyser = null;
						controllerClassAnalyser = Repository
								.lookupClass(controllerclass);

						java.lang.reflect.Method[] methods = controllerclass
								.getDeclaredMethods();

						for (java.lang.reflect.Method method : methods)
						{
							AnnotatedElement el = method;

							// && !method.getName().equals("<init>")
							if (Modifier.isPublic(method.getModifiers())
									&& el.isAnnotationPresent(Get.class)
									|| el.isAnnotationPresent(Post.class)
									|| el.isAnnotationPresent(Head.class)
									|| el.isAnnotationPresent(Put.class)
									|| el.isAnnotationPresent(Delete.class))
							{

								MyLog.info(method.toGenericString());

								JaneMVCMethod janemethod = new JaneMVCMethod();
								janemethod.setMethod(method);
								janemethod.setControllerName(fullclassname);

								janemethod
										.setReturnType(method.getReturnType());

								janemethod.setParameterTypes(method
										.getParameterTypes());

								janemethod.setAnnotations(method
										.getAnnotations());

								janemethod.setParameterAnnotations(method
										.getParameterAnnotations());

								String webname = String
										.format("/%s/%s",
												utils.getClassBaseName(
														utils.getControllerIdentifier(fullclassname))
														.toLowerCase(),
												janemethod.getMethodName()
														.toLowerCase());

								this.methodsByWebname.put(webname, janemethod);
								janemethod.setWebname(webname);

								// Log.loginfo(janemethod.getMethodName());
								// Log.loginfo(janemethod.getWebname());
							}
						}

					}

				}

				if (element.getName().equals("mvc/models/"))
				{
					hasModelsDir = true;
				} else if (element.getName().startsWith("mvc/models/")
						&& element.getName().endsWith(".class"))
				{
					modelClassesInApplication.add(utils
							.jarEntryToJavaName(element.getName()));
				}

				if (element.getName().equals("Views/"))
				{
					hasViewsDir = true;
				} else if (element.getName().startsWith("Views/")
						&& element.getName().endsWith(".jane"))
				{
					janeViewsFileNamesInApplication.add(element.getName());
				}

				allItemsInApplication.add(element.getName());

			}

			if (hasConfigDir && hasControllersDir && hasModelsDir
					&& hasViewsDir)
			{

				valid = true;
			}

		}

		try
		{
			jar.close();
		} catch (IOException e)
		{
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

		return valid;
	}
}
